package com.zh.auth.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.auth.domain.dto.AddPermissionDto;
import com.zh.auth.domain.dto.PagePermissionDto;
import com.zh.auth.domain.dto.UpdatePermissionDto;
import com.zh.auth.mapper.PermissionMapper;
import com.zh.auth.mapper.RolePermissionMapper;
import com.zh.auth.properties.RedisKeyProperties;
import com.zh.auth.service.PermissionService;
import com.zh.auth.service.RolePermissionService;
import com.zh.common.exception.BusinessException;
import com.zh.common.utils.RedisUtil;
import com.zh.web.base.PageApiResult;
import com.zh.web.domain.auth.entity.Permission;
import com.zh.web.domain.auth.entity.RolePermission;
import com.zh.web.enums.ProtectedPermission;
import com.zh.web.enums.Status;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {

    private final PermissionMapper permissionMapper;
    private RolePermissionService rolePermissionService;
    private final RolePermissionMapper rolePermissionMapper;
    private final RedisUtil redisUtil;
    private final RedisKeyProperties redisKeyProperties;

    PermissionServiceImpl(PermissionMapper permissionMapper,
                          @Lazy RolePermissionService rolePermissionService,
                          RolePermissionMapper rolePermissionMapper,
                          RedisUtil redisUtil,
                          RedisKeyProperties redisKeyProperties) {
        this.permissionMapper = permissionMapper;
        this.rolePermissionService = rolePermissionService;
        this.rolePermissionMapper = rolePermissionMapper;
        this.redisUtil = redisUtil;
        this.redisKeyProperties = redisKeyProperties;
    }

    @Override
    @Transactional
    public String addPermission(AddPermissionDto addPermissionDto) {
        PermissionService proxy = (PermissionService) AopContext.currentProxy();
        Permission permission = BeanUtil.copyProperties(addPermissionDto, Permission.class);
        Integer selectCount = permissionMapper.selectCountByCode(addPermissionDto.getPermissionCode());
        if (selectCount > 0) {
            throw new BusinessException("权限已存在");
        }
        proxy.removeIfMarkedAsDeleted(List.of(addPermissionDto.getPermissionCode()));
        return permissionMapper.insert(permission) > 0 ? "添加成功" : "添加失败";
    }

    @Override
    @Transactional
    public String updatePermission(UpdatePermissionDto updatePermissionDto) {
        PermissionService proxy = (PermissionService) AopContext.currentProxy();
        Permission permission = BeanUtil.copyProperties(updatePermissionDto, Permission.class);
        //是否存在该权限
        Permission oldPermission = Optional.of(permissionMapper.selectById(updatePermissionDto.getId()))
                .orElseThrow(() -> new BusinessException("权限不存在"));
        //保护性权限编码/名称不可修改 发生修改编码或者名称操作,检测是否修改前后是否为保护性权限
        if ((ObjUtil.isNotEmpty(updatePermissionDto.getPermissionCode()) && !updatePermissionDto.getPermissionCode().equals(oldPermission.getPermissionCode())) ||
                (ObjUtil.isNotEmpty(updatePermissionDto.getPermissionName()) && !updatePermissionDto.getPermissionName().equals(oldPermission.getPermissionName()))
        ) {
            List<ProtectedPermission> collect = Arrays.stream(ProtectedPermission.values())
                    .filter(protectedPermission -> (protectedPermission.getPermissionCode().equals(updatePermissionDto.getPermissionCode()) ||
                            protectedPermission.getPermissionName().equals(updatePermissionDto.getPermissionName())) ||
                            protectedPermission.getPermissionCode().equals(oldPermission.getPermissionCode()) ||
                            protectedPermission.getPermissionName().equals(oldPermission.getPermissionName())
                    ).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(collect)) {
                String warning = collect.stream().map(protectedPermission ->
                        String.format(("%d:%s"), protectedPermission.getPermissionCode(), protectedPermission.getPermissionName())
                ).collect(Collectors.joining(",", "[", "]"));
                log.warn("修改权限编码或名称为保护性权限,请勿修改,{}", warning);
                String sb = "保护性权限" + "无法修改" + warning;
                throw new BusinessException(sb);
            }
        }
        //权限编码是否重复
        Optional.ofNullable(proxy.getByCode(updatePermissionDto.getPermissionCode()))
                .ifPresent(permission1 -> {
                    throw new BusinessException("权限编码" + permission1.getPermissionCode() + "重复");
                });
        //更新权限,获取最新权限
        boolean updated = permissionMapper.updateById(permission) > 0;
        Permission updatedPermission = Optional
                .of(permissionMapper.selectById(permission.getId()))
                .orElseThrow(() -> new BusinessException("更新失败"));
        //同步角色权限关联表编码
        if (ObjUtil.isNotEmpty(updatedPermission.getPermissionCode())) {
            LambdaUpdateWrapper<RolePermission> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(RolePermission::getPermissionCode, oldPermission.getPermissionCode());
            updateWrapper.set(ObjUtil.isNotEmpty(updatedPermission.getPermissionCode()), RolePermission::getPermissionCode, updatedPermission.getPermissionCode());
            RolePermission rolePermission = new RolePermission();
            rolePermissionMapper.update(rolePermission, updateWrapper);
        }
        //全部完成
        return updated ? "修改成功" : "修改失败";
    }

    @Override
    public PageApiResult<Permission> pagePermission(PagePermissionDto pagePermissionDto) {
        pagePermissionDto.setPageNum(pagePermissionDto.getPageNum() > 0 ? pagePermissionDto.getPageNum() : 1);
        pagePermissionDto.setPageSize(pagePermissionDto.getPageSize() > 0 ? pagePermissionDto.getPageSize() : 10);
        IPage<Permission> page = new Page<>(pagePermissionDto.getPageNum(), pagePermissionDto.getPageSize());
        List<Permission> listPermission = permissionMapper.listPermission(page, pagePermissionDto);
        page.setRecords(listPermission);

        return PageApiResult
                .<Permission>builder()
                .pageNum(page.getCurrent())
                .pageSize(page.getSize())
                .total(page.getTotal())
                .list(page.getRecords())
                .build();
    }

    @Override
    public List<Permission> listPermission() {
        List<Permission> permissionList = redisUtil.getList(redisKeyProperties.getPermissionList(), Permission.class);
        if (CollUtil.isNotEmpty(permissionList)) {
            return permissionList;
        }
        permissionList = permissionMapper.selectList(null);
        redisUtil.setList(redisKeyProperties.getPermissionList(), permissionList);
        return permissionList;
    }

    @Override
    @Transactional
    public String deletePermission(List<Long> ids) {
        //校验参数
        if (CollUtil.isEmpty(ids)) {
            throw new BusinessException("参数不能为空");
        }
        LambdaQueryWrapper<Permission> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(Permission::getId, ids);
        List<Permission> permissionList = permissionMapper.selectList(queryWrapper);
        //保护性权限不能删除
        Set<Integer> protectedPermissionCodeSet = Arrays.stream(ProtectedPermission.values()).map(ProtectedPermission::getPermissionCode).collect(Collectors.toSet());
        List<Permission> checkedPermission = permissionList.stream()
                .filter(permission -> protectedPermissionCodeSet.contains(permission.getPermissionCode()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(checkedPermission)) {
            String warning = checkedPermission.stream().map(protectedPermission ->
                    String.format(("%d:%s"), protectedPermission.getPermissionCode(), protectedPermission.getPermissionName())
            ).collect(Collectors.joining(",", "[", "]"));
            String sb = "存在保护性权限" + "无法删除" + warning;
            log.warn("修改权限编码或名称为保护性权限,请勿修改,{}", warning);
            throw new BusinessException(sb);
        }
        //检查是否存在仍启用的权限
        List<Permission> enablePermissionList = permissionList.stream()
                .filter(permission -> Status.ENABLE.equals(permission.getStatus()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(enablePermissionList)) {
            throw new BusinessException("已启用权限 无法删除");
        }

        //检验是否存在角色关联的权限
        Set<Integer> codes = permissionList.stream()
                .map(Permission::getPermissionCode)
                .collect(Collectors.toSet());
        List<RolePermission> rolePermissionList = rolePermissionService.listRolePermission();
        List<RolePermission> usedPermissionList = rolePermissionList.stream()
                .filter(rolePermission -> codes.contains(rolePermission.getPermissionCode()))
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(usedPermissionList)) {
            StringBuilder sb = new StringBuilder();
            String roles = Arrays.toString(usedPermissionList.stream()
                    .map(RolePermission::getRoleCode).toArray());
            String permissions = Arrays.toString(usedPermissionList.stream()
                    .map(RolePermission::getPermissionCode).toArray());
            sb.append("删除失败:角色 ")
                    .append(roles)
                    .append("关联权限 ")
                    .append(permissions);
            throw new BusinessException(sb.toString());
        }
        return permissionMapper.deleteBatchIds(ids) > 0 ? "删除成功" : "删除失败";
    }

    @Override
    public Permission getByCode(Integer code) {
        LambdaQueryWrapper<Permission> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Permission::getPermissionCode, code);
        return permissionMapper.selectOne(queryWrapper);
    }

    /**
     * 查询权限编码与权限名称的映射
     *
     * @return 权限编码与权限名称的映射
     */
    public Map<String, Integer> getPermissionName2Code() {
        Map<String, Integer> permiName2CodeMap = new HashMap<>();
        List<Map<String, Object>> mapList = permissionMapper.findPermissionName2Code();
        mapList.forEach(map -> {
            String permissionName = (String) map.get("permission_name");
            Integer permissionCode = (Integer) map.get("permission_code");
            permiName2CodeMap.put(permissionName, permissionCode);
        });
        return permiName2CodeMap;
    }

    public Integer removeIfMarkedAsDeleted(List<Integer> codes) {
        return permissionMapper.permanentlyDeleteByIds(codes);
    }
}
